unit frmMedax ;

interface

uses
    Windows,
    Messages,
    SysUtils,
    Classes,
    Graphics,
    Controls,
    Forms,
    Dialogs,
    ActnList,
    ComCtrls,
    ToolWin,
    Menus,
    ImgList,
    ExtCtrls,
    dtpDocument,
    dtpPage,
    dtpShape,
    dtpTextShape,
    StdCtrls,
    dtpExposedMetafile,
    ExtDlgs,
    Gr32v1_5,
    dtpGroupShape,
    OptionRefs,
    Printers,
    dtpEffectShape,
    dtpColoreffects,
    dtpShadowEffects,
    ColorPickerButton,
    dtpBitmapShape,
    dtpRasterFormats,
    Jpeg,
    Math,
    ShellApi,
    dtpPolygonShape,
    dtpMemoShape,
    dtpLineShape,
    dtpResource,
    XPMan,
    VTHeaderPopup,
    VirtualTrees,
    VirtualScrollbox,
    ShowTree,
    Commands,
    ShapeComm,
    PJVersionInfo ;

type

    // A quick way to publish some protected properties we need to set
    THackControl = class( TControl )
    published
        property DragCursor ;
    end ;

    TfrmMain = class( TForm )
        ToolBar : TToolBar ;
        StatusBar : TStatusBar ;
        SplitterVert : TSplitter ;
        VTHeaderPopupMenu1 : TVTHeaderPopupMenu ;
        ToolButton1 : TToolButton ;
        ToolButton2 : TToolButton ;
        ImageList : TImageList ;
        MainMenu1 : TMainMenu ;
        ToolButton3 : TToolButton ;
        ToolButton4 : TToolButton ;
        ToolButton5 : TToolButton ;
        ToolButton6 : TToolButton ;
        ToolButton7 : TToolButton ;
        ToolButton8 : TToolButton ;
        ToolButton10 : TToolButton ;
        ToolButton12 : TToolButton ;
        XPManifest : TXPManifest ;
        File2 : TMenuItem ;
        Open1 : TMenuItem ;
        New1 : TMenuItem ;
        Save1 : TMenuItem ;
        SaveAs1 : TMenuItem ;
        Print1 : TMenuItem ;
        Printcurrentpage1 : TMenuItem ;
        N2 : TMenuItem ;
        acExit1 : TMenuItem ;
        Edit1 : TMenuItem ;
        Undo1 : TMenuItem ;
        Redo1 : TMenuItem ;
        N3 : TMenuItem ;
        Cut1 : TMenuItem ;
        Copy1 : TMenuItem ;
        Paste1 : TMenuItem ;
        Shape1 : TMenuItem ;
        Addshape1 : TMenuItem ;
        AddTextShape1 : TMenuItem ;
        AddMemoShape1 : TMenuItem ;
        AddPhotoShape1 : TMenuItem ;
        AddMetafile1 : TMenuItem ;
        AddLineShape1 : TMenuItem ;
        AddEllipse1 : TMenuItem ;
        AddRectangle1 : TMenuItem ;
        AddRoundedRectangle1 : TMenuItem ;
        DeleteShapes1 : TMenuItem ;
        N4 : TMenuItem ;
        MoveBack1 : TMenuItem ;
        MoveFwd1 : TMenuItem ;
        ToBkgnd1 : TMenuItem ;
        ToFgnd1 : TMenuItem ;
        N5 : TMenuItem ;
        Group1 : TMenuItem ;
        Ungroup1 : TMenuItem ;
        Align1 : TMenuItem ;
        Align2 : TMenuItem ;
        AlignLeft1 : TMenuItem ;
        AlignCenter1 : TMenuItem ;
        AlignRight1 : TMenuItem ;
        AlignTop1 : TMenuItem ;
        AlignMiddle1 : TMenuItem ;
        AlignBottom1 : TMenuItem ;
        Page2 : TMenuItem ;
        Addnewpageatend1 : TMenuItem ;
        Insertpagehere1 : TMenuItem ;
        EditPageName1 : TMenuItem ;
        DeletePage1 : TMenuItem ;
        N6 : TMenuItem ;
        NextPage1 : TMenuItem ;
        PreviousPage1 : TMenuItem ;
        View2 : TMenuItem ;
        acViewThumbs1 : TMenuItem ;
        N7 : TMenuItem ;
        ZoomWidth2 : TMenuItem ;
        ZoomPage2 : TMenuItem ;
        ZoomIn2 : TMenuItem ;
        Zoomout2 : TMenuItem ;
        N8 : TMenuItem ;
        ShowMargin1 : TMenuItem ;
        N9 : TMenuItem ;
        PrintLayout1 : TMenuItem ;
        AddTextShape2 : TMenuItem ;
        N10 : TMenuItem ;
        NoHelpers1 : TMenuItem ;
        acHelpersDots1 : TMenuItem ;
        ShowGrid1 : TMenuItem ;
        acSnapToGrid1 : TMenuItem ;
        N11 : TMenuItem ;
        AutomaticThumbnailupdate1 : TMenuItem ;
        HotzoneScrolling1 : TMenuItem ;
        Renderatscreenresolution1 : TMenuItem ;
        Help2 : TMenuItem ;
        Contents1 : TMenuItem ;
        N12 : TMenuItem ;
        About2 : TMenuItem ;
        ToolButton11 : TToolButton ;
        ToolButton13 : TToolButton ;
        ToolButton14 : TToolButton ;
        ToolButton15 : TToolButton ;
        ToolButton17 : TToolButton ;
        ToolButton20 : TToolButton ;
        ToolButton21 : TToolButton ;
        ToolButton22 : TToolButton ;
        ilThumbs : TImageList ;
        ToolButton23 : TToolButton ;
        ToolButton24 : TToolButton ;
        ToolButton25 : TToolButton ;
        ToolButton26 : TToolButton ;
        ToolButton27 : TToolButton ;
        ToolButton28 : TToolButton ;
        ToolButton29 : TToolButton ;
        ToolButton30 : TToolButton ;
        ActionList1 : TActionList ;
        actForm : TAction ;
        PJVersionInfo : TPJVersionInfo ;
        PanelDoc : TPanel ;
        PageControlTools : TPageControl ;
        TabSheetTree : TTabSheet ;
        VirtualStringTreeShapes : TVirtualStringTree ;
        TabSheetTools : TTabSheet ;
        GroupBoxP1 : TGroupBox ;
        GroupBoxP2 : TGroupBox ;
        ToolButton31 : TToolButton ;
        ToolButtonAdd : TToolButton ;
        PageControlDraw : TPageControl ;
        TabSheetSchema : TTabSheet ;
        TabSheetPCB : TTabSheet ;
        fDocumentSchema : TdtpComponent ;
        fDocumentPCB : TdtpComponent ;
        ListViewThumbs : TListView ;
        SplitterThumbs : TSplitter ;
        procedure ListViewThumbsDragOver( Sender, Source : TObject ; X,
            Y : Integer ; State : TDragState ; var Accept : Boolean ) ;
        procedure ListViewThumbsClick( Sender : TObject ) ;
        procedure ListViewThumbsChange( Sender : TObject ; Item : TListItem ;
            Change : TItemChange ) ;
        procedure ListViewThumbsDragDrop( Sender, Source : TObject ; X,
            Y : Integer ) ;
        procedure ListViewThumbsData( Sender : TObject ; Item : TListItem ) ;
        procedure ListViewThumbsEdited( Sender : TObject ; Item : TListItem ;
            var S : string ) ;
        procedure ListViewThumbsExit( Sender : TObject ) ;
        procedure ListViewThumbsKeyDown( Sender : TObject ; var Key : Word ;
            Shift : TShiftState ) ;
        procedure ListViewThumbsStartDrag( Sender : TObject ;
            var DragObject : TDragObject ) ;
        procedure fDocumentSchemaDragDrop( Sender, Source : TObject ; X, Y : Integer ) ;
        procedure fDocumentSchemaDragOver( Sender, Source : TObject ; X, Y : Integer ;
            State : TDragState ; var Accept : Boolean ) ;
        procedure fDocumentSchemaStartDrag( Sender : TObject ;
            var DragObject : TDragObject ) ;
        procedure fDocumentSchemaProgress( Sender : TObject ; AType : TdtpProgressType ;
            ACount, ATotal : Integer ; APercent : Single ) ;
        procedure fDocumentSchemaPageChanged( Sender : TObject ; Page : TdtpPage ) ;
        procedure FormCreate( Sender : TObject ) ;
        procedure FormCloseQuery( Sender : TObject ; var CanClose : Boolean ) ;
        procedure fDocumentSchemaSelectionChanged( Sender : TObject ) ;
        procedure actFormUpdate( Sender : TObject ) ;
        procedure FormActivate( Sender : TObject ) ;
        procedure fDocumentSchemaShapeChanged( Sender : TObject ; Shape : TdtpShape ) ;
        procedure VirtualStringTreeShapesChange( Sender : TBaseVirtualTree ; Node : PVirtualNode ) ;
        procedure fDocumentPCBSelectionChanged( Sender : TObject ) ;
    private
        { Private declarations }
        FDragClass : TClass ;                               // This is the class of the dragged object
        FDragObject : TObject ;                             // This is the dragged object
        FDragIndex : integer ;                              // This is the index of the dragged item (if any), or -1
        FDragPage : integer ;                               // The page index from where we drag
        FDragSource : TObject ;
        fFilename : string ;
        fSelectionCountSchema : integer ;
        fSelectionCountPCB : integer ;
        fTree : TShowTree ;
        procedure SetStatusText( AText : string ) ;
        function GetFilename : string ;
        procedure SetFilename( const Value : string ) ;
        function GetSelectionCount : integer ;
    public
        { Public declarations }
        property DocumentSchema : TdtpComponent read fDocumentSchema ;
        property DocumentPCB : TdtpComponent read fDocumentPCB ;
        function AskSaveDocument : TModalResult ;
        property SelectionCount : integer read GetSelectionCount ;
        procedure DeleteSelectedShapes ;
        procedure DeleteSelectedPages ;
        property Thumbs : TListView read ListViewThumbs ;
        property DocFilename : string read GetFilename write SetFilename ;
        procedure LoadDocument( const aFilename : string ) ;
        procedure SaveDocument ;
    end ;

var
    frmMain         : TfrmMain ;

const
    cApplicationName = 'mEDA' ;
    cUntitled       = 'Untitled' ;
    cDefaultText    = 'Text' ;
    cDtpDocumentFilter = 'Document files (*.mdx)|*.mdx|' + 'All files (*.*)|*.*' ;
    cUserOptionsRegKey = '\Software\Mediatronix\Medax' ;

{$R DROPCURSORS.RES}                                        // Load these additional cursors
const
    // Cursor constants
    crCopy          = 101 ;
    crMove          = 102 ;
    crLink          = 103 ;
    crCopyScroll    = 104 ;
    crMoveScroll    = 105 ;
    crLinkScroll    = 106 ;

implementation

{$R *.dfm}

{ Statusbar }

procedure TfrmMain.SetStatusText( AText : string ) ;
begin
    StatusBar.SimpleText := AText ;
    Application.ProcessMessages ;
end ;

{ ListView handling }

procedure TfrmMain.ListViewThumbsDragOver( Sender, Source : TObject ; X,
    Y : Integer ; State : TDragState ; var Accept : Boolean ) ;
var
    IsCtrl          : boolean ;
    //  ADest: TListItem;
begin
    Accept := ( FDragSource = ListViewThumbs ) or ( FDragClass = TdtpShape ) ;
    // Find keyboard state so that we can choose appropriate mouse cursor
    IsCtrl := ( GetKeyState( VK_CONTROL ) < 0 ) ;
    if FDragSource is TControl then
        if IsCtrl then
            THackControl( FDragSource ).DragCursor := crCopy
        else
            THackControl( FDragSource ).DragCursor := crMove ;
    {  if FDragSource = FDocument then begin
        // Switch to new page while dragging over
        ADest := lvThumbs.GetItemAt(X, Y);
        if assigned(ADest) then
          FDocument.CurrentPageIndex := ADest.Index;
      end;}
end ;

procedure TfrmMain.ListViewThumbsClick( Sender : TObject ) ;
begin
    // User clicks on a thumbnail - make sure to update (because the OnChange does
    // not happen automatically)
    ListViewThumbsChange( Sender, ListViewThumbs.ItemFocused, ctState ) ;
end ;

procedure TfrmMain.ListViewThumbsChange( Sender : TObject ; Item : TListItem ;
    Change : TItemChange ) ;
var
    APageIndex      : integer ;
begin
    if not assigned( Item ) then
        exit ;
    if Change = ctState then begin
        if assigned( ListViewThumbs.ItemFocused ) then begin
            APageIndex := ListViewThumbs.ItemFocused.Index ;
            fDocumentSchema.CurrentPageIndex := APageIndex ;
            if assigned( fDocumentSchema.Pages[ APageIndex ] ) then
                with fDocumentSchema.Pages[ APageIndex ] do
                    if IsThumbModified then
                        UpdateThumbnail ;
        end ;
    end ;
end ;

procedure TfrmMain.ListViewThumbsDragDrop( Sender, Source : TObject ; X,
    Y : Integer ) ;
var
    ADest           : TListItem ;
    ADestIndex      : integer ;
    IsCTRL          : boolean ;

    procedure MoveOrCopy( OldIndex, NewIndex : integer ; IsCopy : boolean ) ;
    begin
        if IsCopy then begin
            fDocumentSchema.PageCopy( OldIndex, NewIndex ) ;
        end
        else begin
            fDocumentSchema.PageMove( OldIndex, NewIndex ) ;
        end ;
    end ;

begin
    // Find destination at position X,Y
    with ListViewThumbs do begin
        ADestIndex := -1 ;
        ADest := GetItemAt( X, Y ) ;
        if not assigned( ADest ) then
            ADest := GetNearestItem( Point( X, Y ), sdAll ) ;
        if assigned( ADest ) then
            ADestIndex := ADest.Index ;
    end ;

    // Get keystate if [CTRL]
    IsCTRL := GetKeyState( VK_CONTROL ) < 0 ;

    // Internal drag/drop?
    if FDragSource = ListViewThumbs then begin
        if ( FDragIndex >= 0 ) and ( FDragIndex < ListViewThumbs.Items.Count ) then begin
            // Find item at position X,Y
            if assigned( ADest ) then
                with ListViewThumbs do begin
                    MoveOrCopy( FDragIndex, ADestIndex, IsCTRL ) ;
                    // focus on the new index
                    Selected := nil ;
                    Selected := Items[ ADestIndex ] ;
                    ItemFocused := Items[ ADestIndex ] ;
                end ;
        end ;
    end ;

    // External, from document?
    if FDragSource = fDocumentSchema then
        with fDocumentSchema do begin
            if ADestIndex <> CurrentPageIndex then begin
                if IsCtrl then
                    Copy
                else
                    Cut ;
                CurrentPageIndex := ADestIndex ;
                Paste ;
            end ;
        end ;

    // Reset
    FDragSource := nil ;
    FDragClass := nil ;
    FDragObject := nil ;
    FDragIndex := -1 ;
end ;

procedure TfrmMain.ListViewThumbsData( Sender : TObject ; Item : TListItem ) ;
var
    APage           : TdtpPage ;
begin
    if not assigned( Item ) then
        exit ;
    APage := fDocumentSchema.Pages[ Item.Index ] ;
    if not assigned( APage ) then
        exit ;
    // Name of the page
    Item.Caption := APage.Name ;
    // Thumbnail
    Item.ImageIndex := APage.PageIndex ;
end ;

procedure TfrmMain.ListViewThumbsEdited( Sender : TObject ; Item : TListItem ;
    var S : string ) ;
// Change the page name after it has been edited in thumbnail view
var
    APage           : TdtpPage ;
begin
    if not assigned( Item ) then
        exit ;
    APage := fDocumentSchema.Pages[ Item.Index ] ;
    if assigned( APage ) then
        APage.Name := S ;
end ;

procedure TfrmMain.ListViewThumbsExit( Sender : TObject ) ;
begin
    // Make sure we set acDelete.enabled correctly when exiting page thumbs
    // UpdateMenu ;
end ;

procedure TfrmMain.ListViewThumbsKeyDown( Sender : TObject ; var Key : Word ;
    Shift : TShiftState ) ;
// Catch key input for thumbnail display here
begin
    case Key of
        VK_F2 : CommandModule.actPageEditName.Execute ;
        VK_DELETE : DeleteSelectedPages ;
        VK_INSERT : CommandModule.actInsertPage.Execute ;
    end ;                                                   //case
end ;

procedure TfrmMain.ListViewThumbsStartDrag( Sender : TObject ;
    var DragObject : TDragObject ) ;
begin
    // Record dragindex
    FDragClass := TListItem ;
    FDragIndex := ListViewThumbs.ItemFocused.Index ;
    FDragObject := ListViewThumbs.ItemFocused ;
    FDragSource := ListViewThumbs ;
end ;

{ Document }

procedure TfrmMain.fDocumentSchemaDragDrop( Sender, Source : TObject ; X,
    Y : Integer ) ;
var
    IsCTRL          : boolean ;
    NewIndex        : integer ;
begin
    // Get keystate if [CTRL]
    IsCTRL := GetKeyState( VK_CONTROL ) < 0 ;

    // Internal drag/drop?
    if FDragSource = fDocumentSchema then
        with fDocumentSchema do begin
            NewIndex := CurrentPageIndex ;
            CurrentPageIndex := FDragPage ;
            if IsCtrl then
                fDocumentSchema.Copy
            else
                fDocumentSchema.Cut ;
            CurrentPageIndex := NewIndex ;
            fDocumentSchema.Paste ;
        end ;

    // Reset
    FDragSource := nil ;
    FDragClass := nil ;
    FDragObject := nil ;
    FDragIndex := -1 ;
    FDragPage := -1 ;
end ;

procedure TfrmMain.fDocumentSchemaDragOver( Sender, Source : TObject ; X,
    Y : Integer ; State : TDragState ; var Accept : Boolean ) ;
var
    IsCtrl          : boolean ;
begin
    // Find keyboard state so that we can choose appropriate mouse cursor
    IsCtrl := ( GetKeyState( VK_CONTROL ) < 0 ) ;

    Accept := ( FDragClass = TdtpShape ) ;
    // The rest is only for internal drag
    if not ( FDragSource = fDocumentSchema ) then
        exit ;

    if IsCtrl then
        fDocumentSchema.DragCursor := crCopy
    else
        fDocumentSchema.DragCursor := crMove ;
end ;

procedure TfrmMain.fDocumentSchemaStartDrag( Sender : TObject ;
    var DragObject : TDragObject ) ;
begin
    fDocumentSchema.DragCursor := crMove ;
    FDragClass := TdtpShape ;
    FDragObject := fDocumentSchema.FocusedShape ;
    FDragPage := fDocumentSchema.CurrentPageIndex ;
    if assigned( fDocumentSchema.FocusedShape ) then
        FDragIndex := fDocumentSchema.FocusedShape.Index
    else
        FDragIndex := -1 ;
    FDragSource := fDocumentSchema ;
end ;

procedure TfrmMain.fDocumentSchemaProgress( Sender : TObject ;
    AType : TdtpProgressType ; ACount, ATotal : Integer ; APercent : Single ) ;
var
    AVerb           : string ;
begin
    case AType of
        ptLoad : AVerb := 'Loading' ;
        ptPrint : AVerb := 'Printing' ;
        ptSave : AVerb := 'Saving' ;
        ptWizard : AVerb := 'Creating' ;
    end ;

    case AType of
        ptLoad, ptPrint, ptSave, ptWizard : begin
                if APercent < 1 then
                    SetStatusText( Format( '%s page %d of %d (%d%%)...',
                        [ AVerb, Acount, ATotal, round( APercent * 100 ) ] ) )
                else
                    SetStatusText( Format( '%s complete', [ AVerb ] ) )
            end ;
    end ;                                                   //case

end ;

procedure TfrmMain.DeleteSelectedPages ;
var
    i               : integer ;
    AList           : TList ;
    // local
    procedure DeleteList ;
    var
        i           : integer ;
    begin
        for i := AList.Count - 1 downto 0 do
            fDocumentSchema.PageDelete( TdtpPage( AList[ i ] ).PageIndex ) ;
    end ;
    // main
begin
    // Get selection
    AList := TList.Create ;
    try
        // Find list of selected pages
        for i := 0 to Thumbs.Items.Count - 1 do
            if Thumbs.Items[ i ].Selected then
                AList.Add( fDocumentSchema.Pages[ i ] ) ;
        // Checks
        if AList.Count = 0 then
            exit ;
        if AList.Count = 1 then
            if MessageDlg( Format(
                'Do you really want to delete %s?', [ TdtpPage( AList[ 0 ] ).Name ] ),
                mtConfirmation, mbYesNoCancel, 0 ) = mrYes then
                // User wants to delete
                DeleteList ;
        if AList.Count > 1 then
            if MessageDlg( Format(
                'Do you really want to delete %d pages?', [ AList.Count ] ),
                mtConfirmation, mbYesNoCancel, 0 ) = mrYes then
                DeleteList ;
    finally
        AList.Free ;
    end ;
end ;

procedure TfrmMain.fDocumentSchemaPageChanged( Sender : TObject ; Page : TdtpPage ) ;
// Something with a page changed, so we must update the page thumbs listview
var
    ALeft, ATop     : integer ;
    ABitmap         : TBitmap ;
    AText           : string ;
begin
    if assigned( Page ) then begin
        // Set listview size correctly if it weren't. It will clear the list if changed
        ilThumbs.Width := fDocumentSchema.ThumbnailWidth ;
        ilThumbs.Height := fDocumentSchema.ThumbnailHeight ;
        // Create new thumbnails in advance
        if Page.PageIndex >= ilThumbs.Count then begin
            // The page needs a thumbnail
            ABitmap := TBitmap.Create ;
            try
                ABitmap.Width := ilThumbs.Width ;
                ABitmap.Height := ilThumbs.Height ;
                // Draw empty thumbnail
                with ABitmap.Canvas do begin
                    Pen.Color := clBlack ;
                    Font.Color := clBlack ;
                    Font.Size := 24 ;
                    Font.Name := 'Arial' ;
                    AText := '?' ;
                    ALeft := ( ABitmap.Width - TextWidth( AText ) ) div 2 ;
                    ATop := ( ABitmap.Height - TextHeight( AText ) ) div 2 ;
                    TextOut( ALeft, ATop, AText ) ;
                end ;
                while Page.PageIndex >= ilThumbs.Count do
                    ilThumbs.Add( ABitmap, nil ) ;
            finally
                ABitmap.Free ;
            end ;
        end ;
        // Update the page's thumbnail in ilThumbs
        if ( Page.PageIndex >= 0 ) and ( Page.PageIndex < ilThumbs.Count ) then
            // Replace the thumbnail at PageIndex
            ilThumbs.Replace( Page.PageIndex, Page.Thumbnail, nil ) ;
    end ;
    // Number of pages
    if ListViewThumbs.Items.Count <> fDocumentSchema.PageCount then
        ListViewThumbs.Items.Count := fDocumentSchema.PageCount ;
    ListViewThumbs.Invalidate ;
end ;

procedure TfrmMain.fDocumentSchemaShapeChanged( Sender : TObject ;
    Shape : TdtpShape ) ;
begin
    if assigned( fTree ) then begin
        fTree.RootNode := fDocumentSchema.CurrentPage ;
        fTree.Invalidate ;
    end ;
end ;

procedure TfrmMain.FormCreate( Sender : TObject ) ;
begin
    // Application title appearing in windows
    Application.Title := cApplicationName ;

    Thumbs.DoubleBuffered := True ;

    // Accept dropped files (ShellAPI)
    DragAcceptFiles( Handle, true ) ;

    with fDocumentSchema do begin
        Clear ;
        DefaultGridSize := 2.54 ;
        DefaultGridColor := clGray ;
        HelperMethod := hmDots ;
        DefaultPageWidth := 297 ;
        DefaultPageHeight := 210 ;
        PageCount := 1 ;
    end ;

    with fDocumentPCB do begin
        Clear ;
        DefaultGridSize := 0.635 ;
        DefaultGridColor := clGray ;
        HelperMethod := hmDots ;
        DefaultPageWidth := 160 ;
        DefaultPageHeight := 100 ;
        PageCount := 1 ;
        ShowMargins := False ;
        Pages[ 0 ].PageColor := clTeal ;
        //        fDocumentPCB.Pages[ 0 ].ShadowMargin := False ;
    end ;

    fTree := TShowTree.Create ;
    fTree.Viewer := VirtualStringTreeShapes ;
end ;

procedure TfrmMain.FormActivate( Sender : TObject ) ;
begin
    WindowState := wsMaximized ;
    fDocumentSchema.ZoomPage ;
end ;

procedure TfrmMain.FormCloseQuery( Sender : TObject ; var CanClose : Boolean ) ;
begin
    if fDocumentSchema.Modified then
        case AskSaveDocument of
            mrYes : CommandModule.actFileSave.Execute ;
            mrNo : ;
            mrCancel : CanClose := False ;
        end ;
end ;

function TfrmMain.AskSaveDocument : TModalResult ;
var
    ADocName, Msg   : string ;
begin
    ADocName := ExtractFileName( DocFileName ) ;
    if length( ADocName ) > 0 then
        Msg := Format( 'Document "%s" has been changed. Do you wish to save changes?', [ ADocName ] )
    else
        Msg := 'The current document has been changed. Do you wish to save changes?' ;
    Result := MessageDlg( Msg, mtWarning, [ mbYes, mbNo, mbCancel ], 0 ) ;
end ;

procedure TfrmMain.LoadDocument( const aFilename : string ) ;
begin
    with fDocumentSchema do begin
        LoadFromFile( aFileName ) ;
        fFileName := aFileName ;
    end ;
    fDocumentSchema.ZoomWidth ;
end ;

procedure TfrmMain.SaveDocument ;
begin
    with fDocumentSchema do begin
        Screen.Cursor := crHourGlass ;
        try
            SaveToFile( DocFileName ) ;
        finally
            Screen.Cursor := crDefault ;
        end ;
    end ;
end ;

function TfrmMain.GetFilename : string ;
begin
    Result := fFilename ;
end ;

procedure TfrmMain.SetFilename( const Value : string ) ;
begin
    fFilename := Value ;
end ;

procedure TfrmMain.fDocumentSchemaSelectionChanged( Sender : TObject ) ;
begin
    fSelectionCountSchema := fDocumentSchema.SelectionCount ;
    if assigned( fTree ) then
        fTree.Invalidate ;
end ;

procedure TfrmMain.fDocumentPCBSelectionChanged( Sender : TObject ) ;
begin
    fSelectionCountPCB := fDocumentPCB.SelectionCount ;
    if assigned( fTree ) then
        fTree.Invalidate ;
end ;

procedure TfrmMain.actFormUpdate( Sender : TObject ) ;
var
    AFileName       : string ;
begin
    SplitterThumbs.Visible := ListViewThumbs.Visible ;
    if Length( DocFileName ) = 0 then
        AFileName := cUntitled
    else
        AFileName := DocFileName ;
    frmMain.Caption := Format( '  %s [%s]', [ cApplicationName, AFileName ] ) ;

    if assigned( fTree ) and ( fTree.RootNode <> fDocumentSchema.CurrentPage ) then
        fTree.RootNode := fDocumentSchema.CurrentPage ;
end ;

procedure TfrmMain.DeleteSelectedShapes ;
begin
    with fDocumentSchema do begin
        if SelectionCount > 1 then
            if MessageDlg( Format( 'Do you really want to delete %d shapes?', [ SelectionCount ] ),
                mtConfirmation, mbYesNoCancel, 0 ) <> mrYes then
                Exit ;
        fDocumentSchema.DeleteSelectedShapes ;
    end ;
end ;

procedure TfrmMain.VirtualStringTreeShapesChange( Sender : TBaseVirtualTree ; Node : PVirtualNode ) ;
begin
    if assigned( fTree ) then
        fTree.SelectNode( Node ) ;
end ;

function TfrmMain.GetSelectionCount : integer ;
begin
    if PageControlDraw.ActivePageIndex = 0 then
        Result := fSelectionCountSchema
    else
        Result := fSelectionCountPCB ;
end ;

initialization
    // Load custom cursors...
    Screen.cursors[ crCopy ] := loadcursor( hinstance, 'DTCOPY' ) ;
    Screen.cursors[ crMove ] := loadcursor( hinstance, 'DTMOVE' ) ;
    Screen.cursors[ crLink ] := loadcursor( hinstance, 'DTLINK' ) ;
    Screen.cursors[ crCopyScroll ] := loadcursor( hinstance, 'DTCOPYSC' ) ;
    Screen.cursors[ crMoveScroll ] := loadcursor( hinstance, 'DTMOVESC' ) ;
    Screen.cursors[ crLinkScroll ] := loadcursor( hinstance, 'DTLINKSC' ) ;

end.

